package tool;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import javax.swing.event.MouseInputAdapter;



/*This ApsectJ file contains the pointcuts which capture important program control points
 * of the inspected GUI Java applications in the folder "inspectedProjects"
 * 
 * Basically, we are interested in capturing specific events with AspectJ inside the code that is ready
   to be inspected. Some of these cases would be:
    A new JComponent is created (e.g. JFrame or JButton) 
     pointcuts: newFrameWindow() and newButton();
    A JFrame is set to visible or called to show up - (e.g. JFrame.setVisible() or JFrame.show()) 
     pointcuts: newWindowShow() and newFrameSetToVisible();
    A JComponent is added to a Container (e.g. a JButton added to a JPanel - JPanel.add(JButton))
   	 pointcuts: captureAddMethod() and captureAddMethod1();
   	 
 * Important fact is the possibility to get information about the SOURCE CODE LINE of the components
 * especially where they are added to a parent component (e.g. JButton added to a JPanel)
 * This is achieved through the JoinPoint.getSourceLocation() functionality provided by AspectJ  	 
 * 
 * The "within(inspectedProjets.*)" inside the pointcuts, means the classes that is specified to capture for,
 * thus in case when we want to inspect any GUI application, we place it on that folder and it automatically 
 * captures the information specified below   	 
 * 
 * Based on the information captured by these pointcuts, we run the algorithm for creating the tree
 * of UI elements in the ComponentTree.java class, which takes as input data provided by this class  	 
 */


public class Aspect {

/*These variables keep the objects captured by aspec pointcuts, mainly JComponents
 * The main algorithm for creating the TREE of UI elements in the "ComponentTree.java" class,
 * takes as input these information and does the rest of the work 
 */
	
	public static JComponent obj; 
	public static JFrame frame; 
	public static JComponent newWindow; 
	public static JComponent btn; 
	public static JComponent frame3;
	public static JComponent newWindow2;
	public static Component glassPane;
	public static String frameName;
    public static MyGlassPane myGlassPane;
    
    public static int clickedLocation;
	public static JComponent lastComponent;
	//In this hashMap and List are saved the data about the captured JComponents
	public static HashMap<Integer, List<String>> capturedData = new HashMap<Integer, List<String>>();
	public static HashMap<Integer, List<String>> capturedShapes = new HashMap<Integer, List<String>>();
	public static List<String> pairList;
	
	public static boolean once = true;
	public static boolean b = true;

	public static aspect Aspect1 {

		// capture in a specific project when a New Button is created
		public pointcut newButton():
		      call(javax.swing.JButton+.new(..)) && within(inspectedProjects.*);

		after() returning (javax.swing.JButton btn) : newButton() {
			obj = btn;
		}
		
		// capture in a specific project when a New Frame is created
		public pointcut newFrameWindow():
		      call(javax.swing.JFrame+.new(..)) && within(inspectedProjects.*);

		after() returning (javax.swing.JFrame f) : newFrameWindow() {
			frame = f;
	
		}

/* capture a JFrame from any class inside the "inspectedProjects" package 
 * which calls the .show(), cast it to JComponent and get its rootPane()
 * so, further we can run the function for creating the tree of UI elements on that JFrame
*/
		pointcut newWindowShow():
			call(* java.awt.Window.show());

		after() : newWindowShow(){
			JFrame f = (JFrame) thisJoinPoint.getTarget();
			JComponent cmp = f.getRootPane();
			frameName = f.getTitle();
			newWindow = cmp;
			System.out.println("Test glass");
	        JCheckBox changeButton =
	                new JCheckBox("Glass pane \"visible\"");
	        changeButton.setSelected(true);
	        changeButton.setSize(new Dimension(30,20));
	        f.getContentPane().add(changeButton, BorderLayout.NORTH);

	 
	        //Set up the glass pane, which appears over both menu bar
	        //and content pane and is an item listener on the change
	        //button.
	        myGlassPane = new MyGlassPane(b, f.getContentPane());
	        myGlassPane.setLayout(new GridBagLayout());
	        changeButton.addItemListener(myGlassPane);
	        f.setGlassPane(myGlassPane);
	        f.setVisible(true);
			
			if (newWindow != null) {
				@SuppressWarnings("unused")
				InspectElement c = new InspectElement(newWindow);
			} else {
				System.out.println("Can not execute the ComponentTree model");
			}
		}

		// Capture when a frame is set to Visible, (same idea as above)
		pointcut newProjecRun(boolean b):
			call(* java.awt.Component.setVisible(..)) && args(b) && within(inspectedProjects.*);

		after(boolean b) : newProjecRun(b){
			if (b == true) {
				//get the Source code line of that Frame
				JFrame f1 = (JFrame) thisJoinPoint.getTarget();
				frameName = f1.getName();
				JComponent cmp = f1.getRootPane();
				newWindow2 = cmp;
				
				if (newWindow2 != null) {
					@SuppressWarnings("unused")
					InspectElement c = new InspectElement(newWindow2);
				} else {
					System.out
							.println("Can not execute the ComponentTree model");
				}
			} else {
				System.out.println("Can not execute the ComponentTree model 2");
			}
		}

/* main PointCut which captures any component added to the container
* e.g. a JButton added to a JPanel, call the infoCaptured() function which returns 
* many information about the inspected component, e.g. if a JButton is captured, show all info
* about it, most important it shows the Source Code Line, where the components
* are added to the panels or the container in general
*/
		pointcut captureAddMethod(JComponent cmp):
			call(* java.awt.Container.add(..)) && args(cmp) && within (inspectedProjects.*);

		@SuppressWarnings({ "deprecation", "unchecked" })
		after(JComponent cmp) : captureAddMethod(cmp) {
			String joinPoint = thisJoinPoint.getSourceLocation().toString();
			infoCaptured(cmp, joinPoint);
		}

		//pointCut for Menu items to be captured
		pointcut captureAddMethodMenu(JComponent cmp):
			call(* javax.swing.JMenuBar.add(..)) && args(cmp) && within (inspectedProjects.*);

		@SuppressWarnings({ "deprecation", "unchecked" })
		after(JComponent cmp) : captureAddMethodMenu(cmp) {
			String joinPoint = thisJoinPoint.getSourceLocation().toString();
			infoCaptured(cmp, joinPoint);
		}
		
		//pointCut for MenuBar to be captured
		pointcut captureAddMethodMenuBar(JComponent cmp):
			call(* javax.swing.JFrame.setJMenuBar(..)) && args(cmp) && within (inspectedProjects.*);

		@SuppressWarnings({ "deprecation", "unchecked" })
		after(JComponent cmp) : captureAddMethodMenuBar(cmp) {
			String joinPoint = thisJoinPoint.getSourceLocation().toString();
			infoCaptured(cmp, joinPoint);
		}

		// specific pointCut for Panel which are added to a top-level component (JFrame)
		public pointcut captureAddFrame(JComponent cmp, String bl):
			call(* java.awt.Container.add(..)) && args(cmp, bl) && within (inspectedProjects.*);
		
		after(JComponent cmp, String bl) : captureAddFrame(cmp, bl) {
			if(cmp instanceof JPanel){
			JPanel pnl = (JPanel) cmp;
			pairList = new ArrayList();
			pairList.add(pnl.getName());
			pairList.add(thisJoinPointStaticPart.getSourceLocation().toString());
			capturedData.put(pnl.hashCode(), pairList);
			capturedShapes.put(cmp.hashCode(), pairList);
			}
		}
		
		// capture new JTable created
		public pointcut newJTable():
		      call(javax.swing.JTable+.new(..)) && within(inspectedProjects.*);

		after() returning (javax.swing.JTable cmp) : newJTable() {
			pairList = new ArrayList();
			pairList.add(cmp.getName());
			pairList.add(thisJoinPoint.getSourceLocation().toString());
			capturedData.put(cmp.hashCode(), pairList);
			capturedShapes.put(cmp.hashCode(), pairList);
		}
		
	}

/* infoCaptured() function returns information about the inspected component, 
*  e.g. if a JButton is captured, show all info about it, 
*  important it shows the Source Code Line, where the components are added
*  to the panels or the container in general add these data to a hashMap, 
*  which will be called to the MAIN PROGRAM, to graphically present the data into a JTable
*  Here all the below specified JComponents are captured and shown in the main program
*/
	public static void infoCaptured(JComponent cmp, String joinPoint) {
		if (cmp instanceof JButton) {
			JButton btn = (JButton) cmp;
			pairList = new ArrayList();
			pairList.add(btn.getLabel());
			insertHashMap(btn,joinPoint);
		}

		if (cmp instanceof JTextField) {
			JTextField txt = (JTextField) cmp;
			pairList = new ArrayList();
			pairList.add(txt.getText());
			insertHashMap(txt,joinPoint);
		}

		if (cmp instanceof JRadioButton) {
			JRadioButton rbtn = (JRadioButton) cmp;
			pairList = new ArrayList();
			pairList.add(rbtn.getLabel());
			insertHashMap(rbtn,joinPoint);
		}

		if (cmp instanceof JCheckBox) {
			JCheckBox chk = (JCheckBox) cmp;
			pairList = new ArrayList();
			pairList.add(chk.getLabel());
			insertHashMap(chk,joinPoint);
		}

		if (cmp instanceof JList) {
			JList lst = (JList) cmp;
			pairList = new ArrayList();
			pairList.add(lst.getSelectedValue().toString());
			insertHashMap(lst,joinPoint);
		}

		if (cmp instanceof JTextArea) {
			JTextArea txtArea = (JTextArea) cmp;
			pairList = new ArrayList();
			pairList.add(txtArea.getSelectedText());
			insertHashMap(txtArea,joinPoint);
		}

		if (cmp instanceof JLabel) {
			JLabel lbl = (JLabel) cmp;
			pairList = new ArrayList();
			pairList.add(lbl.getText());
			insertHashMap(lbl,joinPoint);
		}

		if (cmp instanceof JFileChooser) {
			JFileChooser fc = (JFileChooser) cmp;
			pairList = new ArrayList();
			pairList.add(fc.getDialogTitle());
			insertHashMap(fc,joinPoint);
		}

		if (cmp instanceof JTable) {
			JTable tbl = (JTable) cmp;
			pairList = new ArrayList();
			pairList.add(tbl.getName());
			insertHashMap(tbl,joinPoint);
		}

		if (cmp instanceof JScrollPane) {
			JScrollPane scp = (JScrollPane) cmp;
			pairList = new ArrayList();
			pairList.add(scp.getName());
			insertHashMap(scp,joinPoint);
		}
		
		if (cmp instanceof JComboBox) {
			JComboBox cmb = (JComboBox) cmp;
			pairList = new ArrayList();
			pairList.add(cmb.getName());
			insertHashMap(cmb,joinPoint);
		}
		
		if (cmp instanceof JTextArea) {
			JTextArea txtA = (JTextArea) cmp;
			pairList = new ArrayList();
			pairList.add(txtA.getName());
			insertHashMap(txtA,joinPoint);
		}
		
		if (cmp instanceof JMenuBar) {
			JMenuBar menu = (JMenuBar) cmp;
			pairList = new ArrayList();
			pairList.add(menu.getName());
			insertHashMap(menu,joinPoint);
		}
		
		if (cmp instanceof JMenuItem) {
			JMenuItem menuI = (JMenuItem) cmp;
			pairList = new ArrayList();
			pairList.add(menuI.getName());
			insertHashMap(menuI,joinPoint);
		}
		
		if (cmp instanceof JSlider) {
			JSlider sld = (JSlider) cmp;
			pairList = new ArrayList();
			pairList.add(sld.getName());
			insertHashMap(sld,joinPoint);
		}
	}
	//function for inserting common values of all components
	public static void insertHashMap(JComponent cmp, String joinPoint){
		pairList.add(joinPoint);
		capturedData.put(cmp.hashCode(), pairList);	
		capturedShapes.put(cmp.hashCode(), pairList);
	}

}
class MyGlassPane extends JComponent implements ItemListener {
	Point point;

	// React to change button clicks.
	public void itemStateChanged(ItemEvent e) {
		setVisible(e.getStateChange() == ItemEvent.SELECTED);
	}

	protected void paintComponent(Graphics g) {
		if (point != null) {
			g.setColor(Color.red);
			g.fillOval(point.x - 10, point.y - 10, 20, 20);
		}
	}

	public void setPoint(Point p) {
		point = p;
	}

	public MyGlassPane(boolean b,
			Container contentPane) {
		CBListener listener = new CBListener(b, this,
				contentPane);
		addMouseListener(listener);
		addMouseMotionListener(listener);
	}
}

/**
 * Listen for all events that our check box is likely to be interested in.
 * Redispatch them to the check box.
 */
class CBListener extends MouseInputAdapter {
	Toolkit toolkit;
	Component liveButton;
	MyGlassPane glassPane;
	Container contentPane;

	public CBListener(boolean b,
			MyGlassPane glassPane, Container contentPane) {
		toolkit = Toolkit.getDefaultToolkit();
		this.glassPane = glassPane;
		this.contentPane = contentPane;
		this.glassPane.setBackground(Color.black);
	}

	public void mouseMoved(MouseEvent e) {
		redispatchMouseEvent(e, false);
	}

	public void mouseDragged(MouseEvent e) {
		redispatchMouseEvent(e, false);
	}

	public void mouseClicked(MouseEvent e) {
		redispatchMouseEvent(e, false);
		Point p = e.getLocationOnScreen();
		Aspect.clickedLocation = ("" + p.x + p.y).hashCode();
		
		if(InspectElement.pixelShapes.containsKey(Aspect.clickedLocation)){
			InspectElement.model.setColumnCount(0);
			JComponent cmp = (JComponent) InspectElement.pixelShapes.get(Aspect.clickedLocation);
			PopClickListener.insertInfoTable(cmp, cmp.hashCode());
			
			if(Aspect.lastComponent !=null){
				Aspect.lastComponent.setBackground(null);
				JComponent cmp1 = (JComponent) InspectElement.pixelShapes.get(Aspect.clickedLocation);
				cmp.setBackground(Color.BLUE);
				Aspect.lastComponent = cmp1;
			}
			else{
				JComponent cmp1 = (JComponent) InspectElement.pixelShapes.get(Aspect.clickedLocation);
				cmp.setBackground(Color.BLUE);
				Aspect.lastComponent = cmp1;
			}

			
		}
	}

	public void mouseEntered(MouseEvent e) {
		redispatchMouseEvent(e, false);
	}

	public void mouseExited(MouseEvent e) {
		redispatchMouseEvent(e, false);
	}

	public void mousePressed(MouseEvent e) {
		redispatchMouseEvent(e, false);
	}

	public void mouseReleased(MouseEvent e) {
		redispatchMouseEvent(e, true);
	}

	// A basic implementation of re-dispatching events.
	private void redispatchMouseEvent(MouseEvent e, boolean repaint) {
		Point glassPanePoint = e.getPoint();
		Container container = contentPane;
		Point containerPoint = SwingUtilities.convertPoint(glassPane,
				glassPanePoint, contentPane);
		if (containerPoint.y < 0) { // we're not in the content pane

		} else {
			// The mouse event is probably over the content pane.
			// Find out exactly which component it's over.
			Component component = SwingUtilities.getDeepestComponentAt(
					container, containerPoint.x, containerPoint.y);

			if ((component != null) && (component.equals(liveButton))) {
				// Forward events over the check box.
				Point componentPoint = SwingUtilities.convertPoint(glassPane,
						glassPanePoint, component);
				component
						.dispatchEvent(new MouseEvent(component, e.getID(), e
								.getWhen(), e.getModifiers(), componentPoint.x,
								componentPoint.y, e.getClickCount(), e
										.isPopupTrigger()));
			}
		}

		// Update the glass pane if requested.
		if (repaint) {
			glassPane.setPoint(glassPanePoint);
			glassPane.repaint();
		}
	}

}